/**
 * Copyright (C), 2015-2021, 开度
 * FileName: WebSecurityConfig
 * Author:   ASUS
 * Date:     2021/4/14 10:29
 * Description: 测试
 * History:
 * <author>          <time>          <version>          <desc>
 * Hezeyu           2021/4/14           1.0              测试
 */
package com.example.spring.security.config;

import com.example.spring.security.exception.CustomAccessDeineHandler;
import com.example.spring.security.exception.CustomAuthenticationEntryPoint;
import com.example.spring.security.exception.LogoutSuccess;
import com.example.spring.security.interceptor.menu.MyAccessDecisionService;
import com.example.spring.security.interceptor.menu.MyInvocationSecurityMetadataSourceService;
import com.example.spring.security.interceptor.user.CustomUserDetailsService;
import com.example.spring.security.service.UserInfoServie;
import com.example.spring.security.utils.RequestIdUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

/**
 * 〈测试〉
 *
 * @author ASUS
 * @create 2021/4/14
 * @since 1.0.0
 */
@Configuration
@EnableWebSecurity
////会拦截注解了@PreAuthrize注解的配置.
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    CustomUserDetailsService userDetailsService;

    @Autowired
    UserInfoServie userInfoServie;

    @Autowired
    MyAccessDecisionService myAccessDecisionService;

    @Autowired
    MyInvocationSecurityMetadataSourceService myInvocationSecurityMetadataSourceService;

    private static String REALM = "MY_TEST_REALM";

    @Autowired
    RequestIdUtil requestIdUtil;

    /**
     * 配置路径权限、白名单、认证方式、session等操作
     *
     * @Author:hezeyu
     * @Date: 2021/4/20 17:38
     */
//     .antMatchers("/").permitAll()
//                    .antMatchers("/product/**").hasRole("USER","ADMIN")
//                .antMatchers("/admin/**").hasRole("ADMIN")
//                .antMatchers("/user/*").permitAll()
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        //自定义拦截url 获取当前菜单支持访问的角色列表
                        o.setSecurityMetadataSource(myInvocationSecurityMetadataSourceService);
                        //自定义的权限校验
                        o.setAccessDecisionManager(myAccessDecisionService);
                        return o;
                    }
                })
                // 允许直接访问的地址
                .antMatchers("/users/login").permitAll()
                // 其他地址的访问均需验证权限
                .anyRequest().authenticated()
//                .and()
//                .formLogin()
////                .loginProcessingUrl("/user/authenticate")
//                .successForwardUrl("/user/main")
//                .loginPage("/user/login")
                .and()
                //退出登录相关配置
                .logout()
                //自定义退出登录页面
                .logoutUrl("/user/signOut")
                //退出成功后要做的操作（如记录日志），和logoutSuccessUrl互斥
                .logoutSuccessHandler(new LogoutSuccess())

                .and()
                .httpBasic().realmName(REALM).authenticationEntryPoint(getBasicAuthEntryPoint())
                .and()

                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().apply(securityConfigurerAdapter());
        http.exceptionHandling().accessDeniedHandler(new CustomAccessDeineHandler());
        http.csrf().disable();
    }

    /**
     * 认证用户信息、权限
     *
     * @since: 1.0.0
     * @Author:hezeyu
     * @Date: 2021/4/20 17:35
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 设置自定义的userDetailsService
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    /**
     *
     * 白名单
     * @param web
     * @return:void
     * @since: 1.0.0
     * @Author:hezeyu
     * @Date: 2021/8/18 14:43
     * @throws
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/swagger/**")
                .antMatchers("/swagger-ui.html")
                .antMatchers("/webjars/**")
                .antMatchers("/v2/**")
                .antMatchers("/swagger-resources/**")
                .antMatchers("/doc.html")
                .antMatchers("encrypt/**");
    }

    /**
     * 声明过滤器，上面http注入
     *
     * @since: 1.0.0
     * @Author:hezeyu
     * @Date: 2021/4/20 17:33
     */
    private MyAuthTokenConfigurer securityConfigurerAdapter() {
        return new MyAuthTokenConfigurer(userDetailsService,requestIdUtil);
    }


    /**
     * 注册密码校验方式
     *
     * @since: 1.0.0
     * @Author:hezeyu
     * @Date: 2021/4/20 17:33
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 认证管理器
     *
     * @since: 1.0.0
     * @Author:hezeyu
     * @Date: 2021/4/20 17:33
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    /**
     * 声明拦截器
     *
     * @since: 1.0.0
     * @Author:hezeyu
     * @Date: 2021/4/20 17:33
     */
    @Bean
    public CustomAuthenticationEntryPoint getBasicAuthEntryPoint() {
        return new CustomAuthenticationEntryPoint();
    }

}
